import Taro from '@tarojs/taro'
import type { AxiosResponse, AxiosRequestConfig } from 'axios'
import buildFullPath from 'axios/lib/core/buildFullPath'
import createError from 'axios/lib/core/createError'

export default function(config: AxiosRequestConfig): Promise<AxiosResponse> {
  return config.filePath ? uploadFile(config) : sendRequest(config)
}

function sendRequest(config: AxiosRequestConfig): Promise<AxiosResponse> {
  return new Promise(function(resolve, reject) {
    const { baseURL, url, timeout, headers, method = 'get', responseType, data, params } = config
    const methodUppercase = method.toUpperCase()
    const option = {
      url: buildFullPath(baseURL, url) + generateSearchParams(params, methodUppercase),
      method: methodUppercase as keyof Taro.request.Method,
      timeout: timeout,
      data: methodUppercase === 'GET' ? params : data,
      responseType: (responseType || '') as keyof Taro.request.ResponseType,
      header: headers || { 'content-type': 'application/json' }
    }

    Taro.request({
      ...option,
      success: ({ data, header, statusCode, errMsg }) => {
        resolve({
          data,
          status: statusCode,
          headers: header,
          config,
          statusText: errMsg
        })
      },
      fail: handleError.bind({ reject, config })
    })
  })
}

function uploadFile(config: AxiosRequestConfig): Promise<AxiosResponse> {
  return new Promise(function(resolve, reject) {
    const { baseURL, url, timeout, headers, responseType, data, params, name = 'file', filePath = '' } = config
    const option = {
      url: buildFullPath(baseURL, url) + generateSearchParams(params),
      filePath,
      timeout: timeout,
      FormData: data,
      name,
      responseType: (responseType || '') as keyof Taro.request.ResponseType,
      header: headers || { 'content-type': 'multipart/form-data' }
    }
    Taro.uploadFile({
      ...option,
      success: ({ data, statusCode, errMsg }) => {
        resolve({
          data,
          status: statusCode,
          headers,
          config,
          statusText: errMsg
        })
      },
      fail: handleError.bind({ reject, config })
    })
  })
}

function handleError(err: Error) {
  console.error('[Axios Error];', err)
  this.reject(createError('Network Fail', this.config, '请求失败'))
}

function generateSearchParams(params: Record<string, any> = {}, method: string = 'POST') {
  const paramKeys = Object.keys(params)
  if (method === 'GET' || paramKeys.length === 0) {
    return ''
  }
  const searchParams = Object.keys(params)
    .map(key => {
      const value = params[key]
      if (value === undefined || value === null) {
        return ''
      }
      return `${key}=${value}`
    })
    .filter(Boolean)
    .join('&')
  return '?' + searchParams
}
